package com.cleaner.base.utils

import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.os.Environment
import com.cleaner.base.appContext
import com.cleaner.base.ktx.toFixed
import java.io.File


object GarbageManager {

    fun scanRubbish(): List<WrapperGarbage.Group> {
        val externalStorageFile = Environment.getExternalStorageDirectory()

        if (!externalStorageFile.isDirectory) return emptyList()

        val map = mutableMapOf<String, WrapperGarbage.Group>()

        externalStorageFile.listFiles()?.forEach { it ->
            if (it.name != "Android") scanRubbish(it)?.onEach {
                val title = when (it) {
                    is Garbage.Apk -> "Apk"
                    is Garbage.Thumbnail -> "Thumbnail"
                    is Garbage.EmptyFile -> "Empty File"
                }
                val group = map[title] ?: WrapperGarbage.Group(title, false, emptyList(), false)
                map[title] = group.copy(title = title, items = group.items.toMutableList().apply {
                    add(WrapperGarbage.Item(it, WrapperGarbage.Item.getDefaultCheck(it)))
                }, check = group.check).apply {
                    adjustmentCheckState()
                }
            }
        }

        return map.toList().map { it.second }
    }

    /**
     * [depth]遍历的深度
     */
    private fun scanRubbish(file: File, depth: Int = 3): List<Garbage>? {
        if (!file.exists() && file.canRead() && file.canWrite() || depth <= 0) return null

        val name = file.name
        val size = file.length()

        if (file.isFile) {
            //空文件
            if (size == 0L) {
                return listOf(Garbage.EmptyFile(file))
            }

            val lowercaseName = name.lowercase()

            //安装包
            if (lowercaseName.lastIndexOf(".apk") != -1 ||
                lowercaseName.lastIndexOf(".aab") != -1
            ) {
                return listOf(Garbage.Apk(file))
            }

            //缩略图
            if (lowercaseName.lastIndexOf(".thumbnails") != -1) {
                return listOf(Garbage.Thumbnail(file))
            }

        }

        val rubbishList = mutableListOf<Garbage>()

        if (file.isDirectory) {

            //空文件夹
            if (file.list().isNullOrEmpty()) {
                return listOf(Garbage.EmptyFile(file))
            }

            file.listFiles()?.forEach { it ->
                scanRubbish(it, depth - 1)?.let { rubbishList.addAll(it) }
            }

        }

        return rubbishList
    }

    fun clearRubbish(rubbishList: List<Garbage>) {
        rubbishList.forEach { deleteFile(it.file) }
    }

    private fun deleteFile(file: File) {
        try {
            if (!file.exists()) return
            if (file.isFile) {
                file.delete()
                return
            }

            if (file.isDirectory) {

                file.listFiles()?.forEach {
                    deleteFile(it)
                }
                if (file.listFiles().isNullOrEmpty()) {
                    file.delete()
                    return
                }
            }

        } catch (e: Throwable) {

        }
    }

    private fun WrapperGarbage.Item.Companion.getDefaultCheck(garbage: Garbage): Boolean = when (garbage) {
        is Garbage.Apk -> true
        is Garbage.EmptyFile -> true
        is Garbage.Thumbnail -> false
    }
}

sealed class Garbage(val file: File) {
    class Apk(file: File) : Garbage(file) {
        fun getApkIcon(): Drawable? {
            if (!file.exists()) return null

            return try {
                val apkPath = file.absolutePath
                val packageManager = appContext.packageManager
                val packageInfo = packageManager.getPackageArchiveInfo(apkPath, PackageManager.GET_ACTIVITIES)
                val applicationInfo = packageInfo?.applicationInfo?.apply {
                    sourceDir = apkPath
                    publicSourceDir = apkPath
                }

                applicationInfo?.loadIcon(packageManager)
            } catch (e: Throwable) {
                null
            }
        }
    }

    class Thumbnail(file: File) : Garbage(file)
    class EmptyFile(file: File) : Garbage(file)

    override fun toString(): String {
        return "${javaClass.name}(file=${file.path})"
    }
}

val Garbage.filename: String get() = file.name
val WrapperGarbage.Item.filename: String get() = garbage.filename

sealed class WrapperGarbage {
    data class Item(val garbage: Garbage, var isCheck: Boolean) : WrapperGarbage() {

        fun getFileSizeTips(): String {
            var size = garbage.file.length()
            var unit = "B"
            while (size / 1024 > 1) {
                size /= 1024
                when (unit) {
                    "B" -> unit = "KB"
                    "KB" -> unit = "MB"
                    "MB" -> {
                        unit = "GB"
                        break
                    }
                }
            }
            return "$size$unit"
        }

        companion object
    }

    data class Group(val title: String, var isShow: Boolean, val items: List<Item>, var check: Boolean?) :
        WrapperGarbage() {

        fun adjustmentCheckState() {
            if (items.isEmpty()) {
                check = false
                return
            }
            val set = items.map { it.isCheck }.toSet()
            if (set.size == 2) {
                check = null
                return
            }
            check = set.firstOrNull()
        }

        fun adjustmentItemState() {
            val check = this.check ?: return
            items.forEach { it.isCheck = check }
        }

        fun getDetailTips(): String {
            var checkSize = 0f
            var totalSize = 0f
            var checkCount = 0

            items.forEach {
                val length = it.garbage.file.length()
                totalSize += length
                if (it.isCheck) {
                    ++checkCount
                    checkSize += length
                }
            }

            var unit = "B"
            while (totalSize / 1024 > 1) {
                totalSize /= 1024
                checkSize /= 1024
                when (unit) {
                    "B" -> unit = "KB"
                    "KB" -> unit = "MB"
                    "MB" -> {
                        unit = "GB"
                        break
                    }
                }
            }

            return "${checkSize.toFixed(1)}$unit/${totalSize.toFixed(1)}$unit • $checkCount/${items.size}"
        }

        companion object
    }
}